//
// Created by 王乐 on 2022/2/7.
//

#include "LeSpriteRenderer.h"
#include "glad/glad.h"

#include "LeVBOManager.h"
#include "LeUtils.h"

#include "LeCameraManager.h"

#include "LeTransform.h"

#include "LeTime.h"

LeSpriteRenderer::LeSpriteRenderer()
{}

LeSpriteRenderer::~LeSpriteRenderer()
{

}

void LeSpriteRenderer::preLoopRender()
{
    unsigned int VBO;
    LeVBOManager::findVBO("Quad", VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);

    m_CurSpriteSheetName = m_MapSpriteSheets.begin()->first;
    m_CurSpriteIndex = 0;
    m_CurSpriteChangeTime = m_MapSpriteSheets[m_CurSpriteSheetName].spriteChangeTime;
    m_bFlipX = false;
}

void LeSpriteRenderer::render()
{
    glm::mat4 projection =  LeCameraManager::getProjectionMatrix();
    glm::mat4 view = LeCameraManager::getViewMatrix();

    // be sure to activate shader when setting uniforms/drawing objects
    m_Shader.useShader();

    m_Shader.setMat4("projection", projection);
    m_Shader.setMat4("view", view);

    glm::mat4 model = m_GameObject->m_Transform.getModelMatrix();
    m_Shader.setMat4("model", model);

    SpriteSheet tempSpriteSheet = m_MapSpriteSheets[m_CurSpriteSheetName];
    m_Shader.setInt("numSprites", tempSpriteSheet.numSprites);

    // Count down changetime.
    m_CurSpriteChangeTime -= LeTime::dDeltaTime;
    if(m_CurSpriteChangeTime <= 0)
    {
        m_CurSpriteChangeTime = tempSpriteSheet.spriteChangeTime;
        m_CurSpriteIndex++;
        m_CurSpriteIndex = m_CurSpriteIndex % tempSpriteSheet.numSprites;
    }
    m_Shader.setInt("curSprite", m_CurSpriteIndex);

    m_Shader.setBool("bFlipX", m_bFlipX);

    m_Shader.setInt("material.diffuse", 0);
    // bind diffuse map
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_MapDiffuseMaps[m_CurSpriteSheetName]);

    // render the cube
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

void LeSpriteRenderer::postLoopRender()
{
    glDeleteVertexArrays(1, &VAO);
}

bool LeSpriteRenderer::changeSpriteSheet(const std::string &name)
{
    auto iter = m_MapSpriteSheets.find(name);
    if(iter != m_MapSpriteSheets.end())
    {
        m_CurSpriteSheetName = iter->first;
        m_CurSpriteIndex = 0;
        m_CurSpriteChangeTime = iter->second.spriteChangeTime;
        return true;
    }
    return false;
}

void LeSpriteRenderer::addSpriteSheet(const std::string &name, SpriteSheet spritesheet)
{
    m_MapSpriteSheets[name] = spritesheet;
    unsigned int diffusemap = LeTextureLoader().loadTexture(spritesheet.diffuseMapPath);
    m_MapDiffuseMaps[name] = diffusemap;
}

void LeSpriteRenderer::setFlipX(bool flipx)
{
    m_bFlipX = flipx;
}

SpriteSheet::SpriteSheet(const char *diffuseMapPath, unsigned int numSprites, float spriteChangeTime) : diffuseMapPath(
        diffuseMapPath), numSprites(numSprites), spriteChangeTime(spriteChangeTime)
{}
